package cloud.tianai.mate.captcha.generator.impl;

import cloud.tianai.captcha.common.constant.CommonConstant;
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.FontWrapper;
import cloud.tianai.captcha.generator.common.model.dto.*;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import lombok.*;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

import static cloud.tianai.mate.captcha.common.constant.MateCaptchaTypeConstant.SCRAPE;

/**
 * @Author: 天爱有情
 * @date 2023/6/15 9:31
 * @Description 刮刮乐
 */
public class MateScrapeImageCaptchaGenerator extends AbstractImageCaptchaGenerator {

    @Getter
    @Setter
    protected Integer iconCount = 10;
    @Getter
    @Setter
    protected FontWrapper font;
    @Getter
    @Setter
    protected int tipImageInterferenceLineNum = 2;
    @Getter
    @Setter
    protected int tipImageInterferencePointNum = 5;

    public MateScrapeImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
        super(imageCaptchaResourceManager);
    }

    public MateScrapeImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform, CaptchaInterceptor interceptor) {
        super(imageCaptchaResourceManager);
        setImageTransform(imageTransform);
        setInterceptor(interceptor);
    }


    @Override
    protected void doInit() {
        if (font == null) {
            // 使用默认字体
            throw new ImageCaptchaException("初始化刮刮乐验证码失败，请设置字体包后再调用init()");
        }
//        ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
//        // 添加一些系统的资源文件
//        resourceStore.addResource(SCRAPE, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
//
//        // 添加icon
//        resourceStore.addResource(CommonConstant.SCRAPE_ICON, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/scrape-icon/包包.png"), DEFAULT_TAG, "包包"));
//        resourceStore.addResource(CommonConstant.SCRAPE_ICON, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/scrape-icon/小猫咪.png"), DEFAULT_TAG, "小猫咪"));
//        resourceStore.addResource(CommonConstant.SCRAPE_ICON, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/scrape-icon/篮球.png"), DEFAULT_TAG, "篮球"));
//        resourceStore.addResource(CommonConstant.SCRAPE_ICON, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/scrape-icon/香蕉.png"), DEFAULT_TAG, "香蕉"));


    }

    @Override
    protected void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
        GenerateParam param = captchaExchange.getParam();
        // 1. 读取icon图 最大3个
        int random = ThreadLocalRandom.current().nextInt(1, iconCount / 2 + 1);
        // 干扰的数量
        int count = iconCount - random + 1;
        int readIconCount = ThreadLocalRandom.current().nextInt(1, count);
        // 读取完要参与的icons
        List<Resource> icons = new ArrayList<>(readIconCount + 1);
        while (icons.size() != readIconCount + 1) {
            Resource resource = requiredRandomGetResource(CommonConstant.SCRAPE_ICON, param.getTemplateImageTag());
            icons.add(resource);
        }
        // 随机取一个icons当做校验
        Resource checkIconResource = icons.remove(ThreadLocalRandom.current().nextInt(0, icons.size()));
        // 干扰的数量
        List<Resource> interferenceIcons = icons;

        List<Resource> allIcons = new ArrayList<>(iconCount);
        for (int i = 0; i < random; i++) {
            allIcons.add(checkIconResource);
        }
        allIcons.addAll(interferenceIcons);
        for (int i = 0; i < iconCount - random - interferenceIcons.size(); i++) {
            allIcons.add(interferenceIcons.get(ThreadLocalRandom.current().nextInt(0, interferenceIcons.size())));
        }
        // 随机打乱
        Collections.shuffle(allIcons);
        // 2. 读取背景图
        Resource bgResource = requiredRandomGetResource(SCRAPE, param.getBackgroundImageTag());
        BufferedImage bgImage = getResourceImage(bgResource);
        // 因为可能有很多重复icon， 加个缓存，避免重复读取
        Map<String, BufferedImage> cache = new HashMap<>(allIcons.size());
        // blocks
        List<Block> blocks = new ArrayList<>();
        // 被使用的blocks
        List<Block> useBlocks = new ArrayList<>();
        for (int i = 0; i < allIcons.size(); i++) {
            Resource resource = allIcons.get(i);
            BufferedImage iconImage = getResourceImage(resource, cache);
            int iconImgWidth = iconImage.getWidth();
            int iconImgHeight = iconImage.getHeight();
            if (i == 0) {
                // 假设每个icon的大小都是一样的, 按照宽高进行分块
                int xNum = (int) Math.floor(bgImage.getWidth() / iconImgWidth);
                int yNum = (int) Math.floor(bgImage.getHeight() / iconImgHeight);
                // 第一列不参与
                for (int x = 1; x < xNum; x++) {
                    for (int y = 0; y < yNum; y++) {
                        blocks.add(new Block(x * iconImgWidth, iconImgWidth, y * iconImgHeight, iconImgHeight, null));
                    }
                }
            }
            Block block = blocks.remove(ThreadLocalRandom.current().nextInt(0, blocks.size()));
            block.setResource(resource);
            useBlocks.add(block);
            CaptchaImageUtils.overlayImage(bgImage, iconImage, block.startX, block.startY);
        }
        // gc回收
        cache = null;
        // 计算 移动位置
        List<Block> sortCheckBlocks = useBlocks.stream().filter(b -> b.getResource().equals(checkIconResource)).sorted(Comparator.comparingInt(Block::getStartX)).collect(Collectors.toList());
        Block posBlock = sortCheckBlocks.get(random - 1);
        captchaExchange.setBackgroundImage(bgImage);
        // 生成提示验证图片
        BufferedImage tipImage = genTipImage(random + "个" + checkIconResource.getTip());
        captchaExchange.setTemplateImage(tipImage);
        captchaExchange.setResourceImage(bgResource);
        captchaExchange.setTransferData(posBlock);
    }

    public BufferedImage genTipImage(String tips) {
        Font font = this.font.getFont();
        float currentFontTopCoef = this.font.getCurrentFontTopCoef();
        // 生成随机颜色
        int fontWidth = tips.length() * font.getSize();
        int width = fontWidth + 6;
        int height = font.getSize() + 6;
        float left = (width - fontWidth) / 2f;
        float top = 6 / 2f + font.getSize() - currentFontTopCoef;
        return CaptchaImageUtils.genSimpleImgCaptcha(tips,
                font, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum);
    }

    protected BufferedImage getResourceImage(Resource resource, Map<String, BufferedImage> cache) {
        return cache.computeIfAbsent(resource.getTip(), t -> getResourceImage(resource));
    }

    @Override
    protected ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
        GenerateParam param = captchaExchange.getParam();
        BufferedImage backgroundImage = captchaExchange.getBackgroundImage();
        BufferedImage tipImage = captchaExchange.getTemplateImage();
        Resource resourceImage = captchaExchange.getResourceImage();
        ResourceMap templateResource = captchaExchange.getTemplateResource();
        CustomData customData = captchaExchange.getCustomData();
        Block posBlock = (Block) captchaExchange.getTransferData();
        ImageTransformData transform = getImageTransform().transform(param, backgroundImage, tipImage, resourceImage, templateResource, customData);
        ImageCaptchaInfo imageCaptchaInfo = new ImageCaptchaInfo();
        // 容错大点
        imageCaptchaInfo.setRandomX(posBlock.startX + 5);
        captchaExchange.getCustomData().putViewData("blockWidth", posBlock.getWidth());
        imageCaptchaInfo.setTolerant(0.05f);
        imageCaptchaInfo.setBackgroundImage(transform.getBackgroundImageUrl());
        imageCaptchaInfo.setTemplateImage(transform.getTemplateImageUrl());
        imageCaptchaInfo.setBackgroundImageTag(resourceImage.getTag());
        imageCaptchaInfo.setTemplateImageTag(param.getTemplateImageTag());
        imageCaptchaInfo.setBackgroundImageWidth(backgroundImage.getWidth());
        imageCaptchaInfo.setBackgroundImageHeight(backgroundImage.getHeight());
        imageCaptchaInfo.setTemplateImageWidth(tipImage.getWidth());
        imageCaptchaInfo.setTemplateImageHeight(tipImage.getHeight());
        imageCaptchaInfo.setType(SCRAPE);
        imageCaptchaInfo.setData(customData);
        return imageCaptchaInfo;
    }


    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class Block {
        private int startX;
        private int width;
        private int startY;
        private int height;
        private Resource resource;
    }
}
